package xj.graph2d;

import static xj.graph2d.DrawShapeConstants.HOTSPOT_EDGE_HEAD_LABEL;
import static xj.graph2d.DrawShapeConstants.HOTSPOT_EDGE_LABEL;
import static xj.graph2d.DrawShapeConstants.HOTSPOT_EDGE_TAIL_LABEL;
import static xj.graph2d.DrawShapeConstants.HOTSPOT_NODE_LABEL;
import static xj.graph2d.DrawShapeConstants.ROLE_DEFAULT;
import static xj.graph2d.DrawShapeConstants.ROLE_EDGE_HEAD_LABEL;
import static xj.graph2d.DrawShapeConstants.ROLE_EDGE_LABEL;
import static xj.graph2d.DrawShapeConstants.ROLE_EDGE_TAIL_LABEL;
import static xj.graph2d.DrawShapeConstants.ROLE_NODE_LABEL;
import static xj.util.xml.XMLUtil.getXMLAttributeString;

import static xj.graph2d.graph.GraphConstants.*;

import java.awt.geom.Rectangle2D;

import xj.graph2d.graph.AbstractGraphComponent;
import xj.graph2d.graph.AbstractGraphEdge;
import xj.graph2d.graph.AbstractGraphNode;
import xj.graph2d.graph.AbstractHyperGraphNode;
import xj.graph2d.graph.AbstractSubgraph;
import xj.graph2d.graph.Graph;
import xj.graph2d.graph.GraphComponent;
import xj.graph2d.graph.Subgraph;
import xj.lang.CodeWriter;

public abstract class GraphComponentShape 
  extends DrawShapeBase 
  implements GraphComponent {

  protected GraphComponentShape() {
    initComponent();
    atomic = false;
  }

  protected GraphComponentShape(int graphComponentType) {
    initComponent(graphComponentType);
    atomic = false;
  }

  protected void initComponent(int graphComponentType) {
    if (graphComponentType == NODE) {
      comp = new AbstractGraphNode();
    } else if (graphComponentType == HYPERNODE) {
      comp = new AbstractHyperGraphNode();
    } else if (graphComponentType == EDGE) {
      comp = new AbstractGraphEdge();
    } else if (graphComponentType == SUBGRAPH) {
      comp = new AbstractSubgraph();
    } else {
      comp = null;
      // comp = new AbstractGraphComponent(graphComponentType);
    }
  }

  public void initComponent() {
    initComponent(null);
  }

  abstract public void initComponent(String compid);

  @Override
  public void copyTo(DrawShapeBase s)
    throws CloneNotSupportedException {
    super.copyTo(s);
    if (s instanceof GraphComponentShape) {
      GraphComponentShape g = (GraphComponentShape) s;
      //g.initComponent(null);
      g.setSubgraph(getSubgraph()); 
      g.acceptLabel = acceptLabel;

      if (s instanceof GraphEdgeBase) { 
	CloneManager.addGraphEdge((GraphEdgeBase) s);
      } else if (s instanceof GraphNodeBase) { 
	CloneManager.addGraphNode(getId(), (GraphNodeBase) s);
      }
    }
  }

  public AbstractGraphComponent getGraphComponent() { 
    return comp;
  }

  public static int hotspotToRole(int hotspot) {
    switch (hotspot) {
    case HOTSPOT_NODE_LABEL:
      return ROLE_NODE_LABEL;

    case HOTSPOT_EDGE_LABEL:
      return ROLE_EDGE_LABEL;

    case HOTSPOT_EDGE_TAIL_LABEL:
      return ROLE_EDGE_TAIL_LABEL;

    case HOTSPOT_EDGE_HEAD_LABEL:
      return ROLE_EDGE_HEAD_LABEL;
    }
    return ROLE_DEFAULT;
  }

  protected Label findLabel(int role) {
    DrawShape s = findInRole(role);
    if (s instanceof Label) {
      return (Label) s;
    }
    return null;
  }

  public Label getLabelShape(int role) {
    return findLabel(role);
  }

  public void setLabel(int role, Label label) {
    setLabel(role, label, false);
  }

  public void setLabelOffset(int role, Label label) {
    setLabel(role, label, true);
  }

  public void setLabel(int role, Label label, boolean offset) {
    if (label != null) {
      removeLabel(role);
      label.setRole(role);
      addShape(label);
      if (offset) {
	setLabelOffset(role);
      } else {
	positionLabel(role);
      }
    }
  }

  public String getLabel(int role) {
    Label label = findLabel(role);
    if (label != null) {
      return label.getText();
    }
    return null;
  }

  abstract public Label getLabelShape();

  abstract public void setLabel(Label label);

  abstract public Label removeLabel(int role);

  abstract public void setLabelOffset(int role);

  abstract public void positionLabel(int role);

  @Override
  public Rectangle2D getRepaintRect() {
    return framebounds;
  }

  @Override
  protected void writeXMLBase(CodeWriter out, int mode, 
			      String namespacePrefix,
			      boolean writeSubshapes) {
    if (out != null) {
      if (comp != null) {
	String nodename;
	nodename = getPrefixedName("GraphComponent", namespacePrefix);
	out.println("<" + nodename + ' ' + 
		    getXMLAttributeString("id", comp.getId()) + "/>");
      }
      super.writeXMLBase(out, mode, namespacePrefix, writeSubshapes);
    }
  }

  @Override
  protected void processXMLElement(org.w3c.dom.Element e, String namespaceURI) {
    if (e != null) {
      String name = e.getLocalName();
      if ("GraphComponent".equals(name)) {
	String compid = e.getAttribute("id");
	initComponent(compid);
      } else {
	super.processXMLElement(e, namespaceURI);
      }
    }
  }

  // adapter
  public boolean isGraphComponent() {
    return (comp != null);
  }

  public boolean isNode() {
    return false;
  }

  public boolean isEdge() {
    return false;
  }

  public boolean isSubgraph() {
    return false;
  }

  public Graph getGraph() {
    if (comp != null) {
      return comp.getGraph();
    }
    return null;
  }

  public Subgraph getSubgraph() {
    if (comp != null) {
      return comp.getSubgraph();
    }
    return null;
  }

  public void setGraph(Graph graph) {
    if (comp != null) {
      comp.setGraph(graph);
    }
  }

  public void setSubgraph(Subgraph subgraph) {
    if (comp != null) {
      comp.setSubgraph(subgraph);
    }
  }

  public void setAttribute(String name, Object value) {
    if (comp != null) {
      comp.setAttribute(name, value);
    }
  }

  public void removeAttribute(String name) {
    if (comp != null) {
      comp.removeAttribute(name);
    }
  }

  public Object getAttribute(String key) {
    if (comp != null) {
      return comp.getAttribute(key);
    }
    return null;
  }

  public boolean hasAttribute(String key) {
    if (comp != null) {
      return comp.hasAttribute(key);
    }
    return false;
  }

  public String getCompLabel() {
    if (comp != null) {
      return comp.getLabel();
    }
    return null;
  }

  public void setCompLabel(String label) {
    if (comp != null) {
      comp.setLabel(label);
    }
  }

  public String getCompName() {
    if (comp != null) {
      return comp.getName();
    }
    return null;
  }

  public void setCompName(String name) {
    if (comp != null) {
      comp.setName(name);
    }
  }

  public double getWeight() {
    if (comp != null) {
      return comp.getWeight();
    }
    return 0.0;
  }

  public void setWeight(double weight) {
    if (comp != null) {
      comp.setWeight(weight);
    }
  }

  public void setMark(int mark, boolean flag) {
    if (comp != null) {
      comp.setMark(mark, flag);
    }
  }

  public boolean getMark(int mark) {
    if (comp != null) {
      return comp.getMark(mark);
    }
    return false;
  }

  public void setAux(Object aux) {
    if (comp != null) {
      comp.setAux(aux);
    }
  }

  public Object getAux() {
    if (comp != null) {
      return comp.getAux();
    }
    return null;
  }

  public void setId(String id) {
    if (comp != null) {
      comp.setId(id);
    }
  }

  public String getId() {
    if (comp != null) {
      return comp.getId();
    }
    return null;
  }

  // full clone mode: whether to clone the comp field of the graph
  // component
  public boolean isFullCloneMode() {
    return fullCloneMode;
  }

  public void setFullCloneMode(boolean fullCloneMode) {
    this.fullCloneMode = fullCloneMode;
  }

  public boolean isAcceptLabel() {
    return acceptLabel;
  }

  public void setAcceptLabel(boolean acceptLabel) {
    this.acceptLabel = acceptLabel;
  }

  protected boolean acceptLabel = true;

  protected boolean fullCloneMode = true;

  protected AbstractGraphComponent comp;

}
